using System;
using System.Collections.Generic;
using UnityEngine;

namespace CHot
{
    public class ColliderManager : MonoBehaviour
    {
        private static ColliderManager _instance;
        public static ColliderManager Instance
        {
            get
            {
                if (!_instance)
                {
                    _instance = GameObject.FindObjectOfType<ColliderManager>();
                    if (!_instance)
                    {
                        GameObject go = new GameObject("[ColliderManager]");
                        DontDestroyOnLoad(go);
                        _instance = go.AddComponent<ColliderManager>();
                    }
                }
                return _instance;
            }
        }

        //仅用于防内存泄露观察
        public int _fishes, _bullets;

        /// <summary>
        /// 外部监听事件(返回的参数可有具体项目需求修改)
        /// </summary>
        public event Action<int, int> onHitEnter, onHitStay, onHitExit;

        private List<ColliderGroup> fishes = new List<ColliderGroup>();

        //能攻击鱼的所有对象（包括子弹，撞船，滚石，钻头，激光）
        private List<ColliderGroup> bullets = new List<ColliderGroup>();

        //隔帧计算
        private int fixedUpdateFrameSwitch;

        //碰撞开始时触发
        public void HitEnter(ColliderGroup bullet, ColliderGroup fish)
        {
            onHitEnter?.Invoke(bullet.gameObject.GetInstanceID(), fish.gameObject.GetInstanceID());
        }

        //碰撞停留时每个物理帧都触发
        public void HitStay(ColliderGroup bullet, ColliderGroup fish)
        {
            onHitStay?.Invoke(bullet.gameObject.GetInstanceID(), fish.gameObject.GetInstanceID());
        }

        //碰撞结束时触发
        public void HitExit(ColliderGroup bullet, ColliderGroup fish)
        {
            onHitExit?.Invoke(bullet.gameObject.GetInstanceID(), fish.gameObject.GetInstanceID());
        }

        private void FixedUpdate()
        {
            if (fixedUpdateFrameSwitch % 2 == 0)
            {
                CheckCollision();
            }
            else
            {
                ListenHitExit();
            }
            fixedUpdateFrameSwitch = (fixedUpdateFrameSwitch + 1) % 2;
        }

        private void CheckCollision()
        {
            for (int i = 0; i < bullets.Count; i++)
            {
                ColliderGroup bullet = bullets[i];
                if (bullet == null || !bullet.isActive) continue;

                for (int j = 0; j < bullet.colliders.Length; j++)
                {
                    bullet.colliders[j].BeforeCheck();
                }

                if (bullet.lockTarget != null && bullet.lockTarget.isActive)
                {
                    //如果子弹是锁定的，只处理锁定的鱼
                    OneBulletCheckOneFish(bullet, bullet.lockTarget);
                    continue;
                }

                //没有锁定鱼，全场鱼计算
                bool isHited = false;
                for (int j = 0; j < fishes.Count; j++)
                {
                    ColliderGroup fish = fishes[j];
                    isHited = OneBulletCheckOneFish(bullet, fish);
                    if (isHited) break;
                }
                if (isHited) continue;
            }
        }

        private void ListenHitExit()
        {
            for (int i = 0; i < fishes.Count; i++)
            {
                fishes[i]._OnExitListen();
            }
            for (int i = 0; i < bullets.Count; i++)
            {
                bullets[i]._OnExitListen();
            }
        }

        private bool OneBulletCheckOneFish(ColliderGroup bullet, ColliderGroup fish)
        {
            for (int i = 0; i < bullet.colliders.Length; i++)
            {
                Collider b_collider = bullet.colliders[i];
                Vector2 b_colliderPos = b_collider.transform.position;
                if (b_collider is CircleCollider)
                {
                    //CircleCollider的判断
                    CircleCollider b_circleCollider = b_collider as CircleCollider;
                    for (int k = 0; k < fish.colliders.Length; k++)
                    {
                        CircleCollider f_collider = fish.colliders[k] as CircleCollider;
                        Vector2 f_colliderPos = f_collider.transform.position;
                        //线碰撞:线到圆心的距离 <（圆半径+子弹半径）
                        float distance = f_collider.radius * f_collider.scale + b_circleCollider.radius;
                        if (PowerOfSegmentToPoint(b_colliderPos, b_circleCollider.lastPos, f_colliderPos) < distance * distance)
                        {
                            bullet._OnHitListen(fish);
                            return true;
                        }
                    }
                }
                else
                {
                    //BoxCollider的判断
                    BoxCollider b_boxCollider = b_collider as BoxCollider;
                    for (int k = 0; k < fish.colliders.Length; k++)
                    {
                        CircleCollider f_collider = fish.colliders[k] as CircleCollider;
                        Vector2 f_pos = f_collider.transform.position;
                        Vector2[] box4Poses = b_boxCollider.Get4Poses();
                        (Vector2 minPos, Vector2 maxPos) = GetMinMaxPos(box4Poses);
                        bool hit = IsBoxAndCircleOverlap(minPos, maxPos, box4Poses, f_pos, f_collider.radius * f_collider.scale);
                        if (hit)
                        {
                            bullet._OnHitListen(fish);
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        // 线段和点的最短距离的平方
        private float PowerOfSegmentToPoint(Vector2 v1, Vector2 v2, Vector2 point)
        {
            float xDis = v2.x - v1.x;
            float yDis = v2.y - v1.y;
            float dx = point.x - v1.x;
            float dy = point.y - v1.y;
            float d = xDis * xDis + yDis * yDis;
            float t = xDis * dx + yDis * dy;//向量点积

            if (d > 0)
            {
                t /= d;
            }

            if (t < 0)
            {
                t = 0;
            }
            else if (t > 1)
            {
                t = 1;
            }
            dx = v1.x + t * xDis - point.x;
            dy = v1.y + t * yDis - point.y;
            return dx * dx + dy * dy;
        }

        //判断圆和矩形是否相交
        //box4Poits顺序：0:左上 1左下 2右下 3右上(左上开始逆时针)
        private bool IsBoxAndCircleOverlap(Vector2 boxMinPos, Vector2 boxMaxPos, Vector2[] box4Poits, Vector2 circleCenter, float circleRadius)
        {
            //判断圆再四点范围外
            if (circleCenter.x + circleRadius < boxMinPos.x ||
                circleCenter.x - circleRadius > boxMaxPos.x ||
                circleCenter.y + circleRadius < boxMinPos.y ||
                circleCenter.y - circleRadius > boxMaxPos.y)
            {
                return false;
            }

            //判断圆心在矩形内部
            Vector2 AB = box4Poits[3] - box4Poits[0];
            Vector2 AE = circleCenter - box4Poits[0];
            Vector2 CD = box4Poits[1] - box4Poits[2];
            Vector2 CE = circleCenter - box4Poits[2];
            Vector2 DA = box4Poits[0] - box4Poits[1];
            Vector2 DE = circleCenter - box4Poits[1];
            Vector2 BC = box4Poits[2] - box4Poits[3];
            Vector2 BE = circleCenter - box4Poits[3];

            if (Vector2.Dot(AB, AE) >= 0 && Vector2.Dot(CD, CE) >= 0 && Vector2.Dot(DA, DE) >= 0 && Vector2.Dot(BC, BE) >= 0)
            {
                return true;
            }

            //判断圆是否与四边相交
            if (PowerOfSegmentToPoint(box4Poits[0], box4Poits[1], circleCenter) < circleRadius * circleRadius)
            {
                return true;
            }
            if (PowerOfSegmentToPoint(box4Poits[1], box4Poits[2], circleCenter) < circleRadius * circleRadius)
            {
                return true;
            }
            if (PowerOfSegmentToPoint(box4Poits[2], box4Poits[3], circleCenter) < circleRadius * circleRadius)
            {
                return true;
            }
            if (PowerOfSegmentToPoint(box4Poits[3], box4Poits[0], circleCenter) < circleRadius * circleRadius)
            {
                return true;
            }
            return false;
        }

        //获取四个顶点的最小顶点和最大顶点
        private (Vector2 minPos, Vector2 maxPos) GetMinMaxPos(Vector2[] poses)
        {
            Vector2 minPos = new Vector2(poses[0].x, poses[0].y);
            Vector2 maxPos = new Vector2(poses[0].x, poses[0].y);
            for (int i = 0; i < poses.Length; i++)
            {
                if (poses[i].x < minPos.x) minPos.x = poses[i].x;
                if (poses[i].y < minPos.y) minPos.y = poses[i].y;
                if (poses[i].x > maxPos.x) maxPos.x = poses[i].x;
                if (poses[i].y > maxPos.y) maxPos.y = poses[i].y;
            }
            return (minPos, maxPos);
        }

        public void Add(ColliderGroup colliderGroup)
        {
            if (colliderGroup.isFish)
            {
                fishes.Add(colliderGroup);
                _fishes++;
            }
            else
            {
                bullets.Add(colliderGroup);
                _bullets++;
            }
        }

        public void Remove(ColliderGroup colliderGroup)
        {
            //子弹更频繁，先判断子弹
            if (bullets.Contains(colliderGroup))
            {
                bullets.Remove(colliderGroup);
                _bullets--;
            }
            else if (fishes.Contains(colliderGroup))
            {
                fishes.Remove(colliderGroup);
                _fishes--;
            }
        }

        public ColliderGroup GetRayHitFish(Vector3 touchWorldPos)
        {
            for (int i = 0; i < fishes.Count; i++)
            {
                ColliderGroup fish = fishes[i];
                for (int j = 0; j < fish.colliders.Length; j++)
                {
                    //已强制鱼只能使用CircleCollider检测
                    CircleCollider collider = fish.colliders[j] as CircleCollider;
                    Vector3 pos = collider.transform.position;
                    float dis = Vector2.Distance(new Vector2(pos.x, pos.y), new Vector2(touchWorldPos.x, touchWorldPos.y));
                    if (dis <= collider.radius * collider.scale)
                    {
                        return fish;
                    }
                }
            }
            return null;
        }

        public void Clear()
        {
            fishes.Clear();
            bullets.Clear();
        }
    }
}